﻿using Dto;
using Entity;
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Mvc;

namespace Logics;

/// <summary>
/// 余额服务
/// </summary>
public class MoneyLogics : ISingleton
{
    private readonly ISqlSugarClient db;

    /// <summary>
    /// 余额
    /// </summary>
    /// <param name="db"></param>
    public MoneyLogics(ISqlSugarClient db)
    {
        this.db = db;
    }


    /// <summary>
    /// 代理余额变更
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<(bool, int)> Proxy(ChangeMoneyAndLog input)
    {
        return await ChangeMoneyAndLog<Proxy, ProxyMoneyLog>(input);
    }

    /// <summary>
    /// 客户余额变更
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<(bool, int)> Client(ChangeMoneyAndLog input)
    {
        return await ChangeMoneyAndLog<Client, ClientMoneyLog>(input);
    }

    /// <summary>
    /// 商户金额日志
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<(bool, int)> Store(ChangeMoneyAndLog input)
    {
        return await ChangeMoneyAndLog<Store, StoreMoneyLog>(input);
    }

    /// <summary>
    /// 变更余额并记录
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TLog"></typeparam>
    /// <returns></returns>
    private async Task<(bool, int)> ChangeMoneyAndLog<T, TLog>(ChangeMoneyAndLog input) where T : MoneyBaseEntity, new() where TLog : MoneyLog, new()
    {
        try
        {
            db.Ado.BeginTran();

            (decimal befor, decimal after, bool state) = await ChangeMoney<T>(input);

            input.Befor = befor;

            input.After = after;

            if (!state) throw new Exception();

            int logId = await AddLog<TLog>(input);

            if (logId == 0) throw new Exception();

            db.Ado.CommitTran();

            return (true, logId);
        }
        catch (Exception)
        {
            db.Ado.RollbackTran();

            return (false, 0);
        }
    }

    /// <summary>
    /// 添加交易记录
    /// </summary>
    /// <returns></returns>
    private async Task<int> AddLog<T>(ChangeMoneyAndLog input) where T : MoneyLog, new()
    {
        var t = new T
        {
            UID = input.Uid,

            After = input.After,

            Befor = input.Befor,

            ChangeType = input.Type,

            MoneyType = input.moneyType,

            Money = input.Price,

            OrderId = input.OrderId
        };

        return await db.Insertable(t).ExecuteReturnIdentityAsync();
    }

    /// <summary>
    /// 变更用户余额
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    private async Task<(decimal, decimal, bool)> ChangeMoney<T>(ChangeMoneyAndLog input) where T : MoneyBaseEntity, new()
    {
        var model = await db.Queryable<T>().Where(x => x.Id == input.Uid).FirstAsync();

        if (model is null)
        {
            RedisHelper.HSet("changeMoneyError", $"{typeof(T).Name}-{input.Uid}", $"编号{input.Uid},订单号:{input.OrderId},金额:{input.Price}");

            return (0, 0, false);
        }

        decimal befor = model.Money;

        decimal after = 0;

        if (input.Type == 0)
        {
            if (befor < input.Price) throw Oops.Oh("账户余额不足");

            after = befor - input.Price;
        }

        else
        {
            after = befor + input.Price;
        }

        model.Money = after;

        bool state = await db.Updateable(model).ExecuteCommandHasChangeAsync();

        return (befor, after, state);
    }
}
